package com.sky.service.impl;

import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.OrdersMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.WorkspaceService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ReportServiceImpl implements ReportService {

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WorkspaceService workspaceService;

    /**
     * 营业额统计
     *
     * @param begin 开始日期
     * @param end   结束日期
     * @return
     */
    @Override
    public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
        // 1、根据起始日期，得到区间内所有日期
        List<LocalDate> dateList = getDateList(begin,end);

        // 2、循环得到日期列表，按照日期依次查询每天的营业额
        // 方式一：
        /*List<Double> turnoverList = new ArrayList<>();
        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            Map paramMap = new HashMap();
            paramMap.put("beginTime",beginTime);
            paramMap.put("endTime",endTime);
            paramMap.put("status", Orders.COMPLETED);

            Double turnover = this.ordersMapper.sumAmountByParams(paramMap);
            turnover = turnover == null ? 0.0:turnover;
            turnoverList.add(turnover);
        }*/

        // 方式二：
        Map paramMap = new HashMap();
        paramMap.put("beginTime",begin);
        paramMap.put("endTime",end);
        paramMap.put("status", Orders.COMPLETED);

        // 分组查询
        List<Map> turnoverMapList = this.ordersMapper.groupSumAmountByParams(paramMap);
        // 将结果转成Map，方便后续根据日期取营业额
        Map<String, BigDecimal> turnoverMap = turnoverMapList.stream().collect(Collectors.toMap(m->(String)m.get("date"), m->(BigDecimal)m.get("amount")));
        List<BigDecimal> turnoverList = new ArrayList<>();
        for (LocalDate date : dateList) {
            BigDecimal amount = turnoverMap.get(date.toString());
            amount = ObjectUtils.isEmpty(amount) ? BigDecimal.valueOf(0.0):amount;
            turnoverList.add(amount);
        }

        // 3、将list转成逗号拼接的字符串
        String dateListStr = StringUtils.join(dateList,",");
        dateListStr = dateList.stream().map(date->date.toString()).collect(Collectors.joining(","));

//        String turnoverListStr = dateList.stream().map(d->d.toString()).collect(Collectors.joining(","));
        String turnoverListStr = StringUtils.join(turnoverList,",");

        //4、组装TurnoverReportVO并返回
        TurnoverReportVO turnoverReportVO = TurnoverReportVO.builder().dateList(dateListStr).turnoverList(turnoverListStr).build();

        return turnoverReportVO;
    }

    /**
     * 统计用户数据
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
        // 获取起止日期间的所有日期
        List<LocalDate> dateList = getDateList(begin, end);
        // 分组统计用户数据
        Map params = new HashMap();
        params.put("beginDate",begin);
        params.put("endDate",end);
        List<Map> userMapList = this.userMapper.groupCountUserByParams(params);
        // 查每日新增用户
        Map<String,Long> userMap = userMapList.stream().collect(Collectors.toMap(m->(String)m.get("date"),m->(Long)m.get("num")));
        // 查截止到今天的用户总数
        params.put("date",begin.minusDays(1));
        params.put("newUserflag",false);
        Integer beginUserCount = this.userMapper.countUserByParams(params);

        List<Integer> totalUserList = new ArrayList<>();
        List<Integer> newUserList = new ArrayList<>();

        for (LocalDate date : dateList) {
            // 当天新增用户数
            Long newUserCount = userMap.get(date.toString());
            newUserCount = newUserCount == null ? 0:newUserCount;
            newUserList.add(newUserCount.intValue());

            // 截止当天用户总数
            beginUserCount += newUserCount.intValue();
            totalUserList.add(beginUserCount);
        }
        // 组装并返回
        UserReportVO userReportVO = UserReportVO.builder()
                                                .dateList(StringUtils.join(dateList,","))
                                                .newUserList(StringUtils.join(newUserList,","))
                                                .totalUserList(StringUtils.join(totalUserList,",")).build();
        return userReportVO;
    }

    @Override
    public OrderReportVO orderStatistics(LocalDate begin, LocalDate end){
        // 获取指定日期范围内所有日期
        List<LocalDate> dateList = getDateList(begin, end);
        // 设置参数，查询当前日期的总订单数
        Map<String,Object> params = new HashMap();
        params.put("beginDate",begin);
        params.put("endDate",end);

        List<Map> totalList = this.ordersMapper.groupCountOrderByParams(params);
        Map<String, Long> totalMap = totalList.stream().collect(Collectors.toMap(m -> (String) m.get("date"), m -> (Long) m.get("sum")));
        // 查询当前日期的有效订单数
        params.put("status",Orders.COMPLETED);
        List<Map> validOrderMapList = this.ordersMapper.groupCountOrderByParams(params);
        Map<String, Long> validOrderMap = validOrderMapList.stream().collect(Collectors.toMap(m -> (String)m.get("date"), m -> (Long) m.get("sum")));

        // 将map转换为字符串，方便组装对象
        ArrayList<Long> orderCountList = new ArrayList<>();
        ArrayList<Long> validOrderList = new ArrayList<>();
        for (LocalDate date : dateList) {
            // 存储每日总订单数
            Long toatlCount = totalMap.get(date.toString());
            toatlCount = toatlCount == null ? 0:toatlCount;
            orderCountList.add(toatlCount);

            // 存储每日有效总订单数
            Long validOrderCount = validOrderMap.get(date.toString());
            validOrderCount = validOrderCount == null ? 0:validOrderCount;
            validOrderList.add(validOrderCount);
        }

        // 获取总订单数
        Long totalOrderCount = orderCountList.stream().mapToLong(oc -> oc).sum();
        // 获取有效订单数
        Long validOrderCount = validOrderList.stream().mapToLong(oc -> oc).sum();
        // 订单完成率
        Double orderCompletionRate = totalOrderCount == 0 ? 0.0:validOrderCount.doubleValue()/totalOrderCount;

        // 组装对象并返回
        return OrderReportVO.builder().dateList(StringUtils.join(dateList,","))
                                        .orderCountList(StringUtils.join(orderCountList,","))
                                        .totalOrderCount(totalOrderCount.intValue())
                                        .validOrderCountList(StringUtils.join(validOrderList,","))
                                        .validOrderCount(validOrderCount.intValue())
                                        .orderCompletionRate(orderCompletionRate)
                                        .build();
    }

    /**
     * 销量榜前十
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO getTop(LocalDate begin, LocalDate end) {
        // 设置参数，查询当前日期菜品或套餐销量前十名
        Map params = new HashMap();
        params.put("beginDate",begin);
        params.put("endDate",end);

        List<GoodsSalesDTO> goodsSalesDTOList = ordersMapper.groupSumNumberByParams(params);
        /*// 将菜品或套餐名称 以及 数量 分别保存起来
        List<String> nameList = goodsSalesDTOs.stream().map(m -> m.getName()).collect(Collectors.toList());
        List<Integer> numberList = goodsSalesDTOs.stream().map(m -> m.getNumber()).collect(Collectors.toList());*/
        //2、转换list，得到所有菜品或套餐名称
        String nameList = goodsSalesDTOList.stream().map(goods -> goods.getName()).collect(Collectors.joining(","));

        //3、转换list，得到所有菜品或套餐销量
        String numberList = goodsSalesDTOList.stream().map(goods -> goods.getNumber().toString()).collect(Collectors.joining(","));
        // 组装对象并返回
        return SalesTop10ReportVO.builder().nameList(nameList).numberList(numberList).build();
    }

    /**
     * 运营数据导出
     *
     * @param response
     */
    @Override
    public void export(HttpServletResponse response) throws IOException, InvalidFormatException {
        //1、确定导出数据的时间范围
        LocalDate startDate = LocalDate.now().minusDays(30);
        LocalDate endDate = LocalDate.now().minusDays(1);

        //2、查询时间区间内的运营数据
        BusinessDataVO businessData = workspaceService.getBusinessData(LocalDateTime.of(startDate, LocalTime.MIN), LocalDateTime.of(endDate, LocalTime.MAX));

        //3、向excel中写入运营数据
        exportStyle(response, startDate, endDate, businessData);
    }

    /**
     * 原生POI导出
     * @param response
     * @param startDate
     * @param endDate
     * @param businessData
     * @throws IOException
     */
    private void exportStyle(HttpServletResponse response, LocalDate startDate, LocalDate endDate, BusinessDataVO businessData) throws IOException {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");
        XSSFWorkbook excel = new XSSFWorkbook(in);
        XSSFSheet sheet1 = excel.getSheetAt(0);

        sheet1.getRow(1).getCell(1).setCellValue("时间：" + startDate + "至" + endDate);
        // 填充第4行的数据
        XSSFRow row4 = sheet1.getRow(3);
        row4.getCell(2).setCellValue(businessData.getTurnover());
        row4.getCell(4).setCellValue(businessData.getOrderCompletionRate());
        row4.getCell(6).setCellValue(businessData.getNewUsers());

        // 填充第5行的数据
        XSSFRow row5 = sheet1.getRow(4);
        row5.getCell(2).setCellValue(businessData.getValidOrderCount());
        row5.getCell(4).setCellValue(businessData.getUnitPrice());


        // 4、查询每日的明细数据，写入到excel中
        List<LocalDate> dateList = this.getDateList(startDate, endDate);
        int rownum = 0;
        for (LocalDate date : dateList) {
            businessData = workspaceService.getBusinessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));
            XSSFRow row = sheet1.getRow(7 + rownum++);
            row.getCell(1).setCellValue(date.toString());
            row.getCell(2).setCellValue(businessData.getTurnover());
            row.getCell(3).setCellValue(businessData.getValidOrderCount());
            row.getCell(4).setCellValue(businessData.getOrderCompletionRate());
            row.getCell(5).setCellValue(businessData.getUnitPrice());
            row.getCell(6).setCellValue(businessData.getNewUsers());
        }
        excel.write(response.getOutputStream());
    }

    private List<LocalDate> getDateList(LocalDate begin, LocalDate end) {
        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(begin);

        while (!begin.equals(end)){
            begin = begin.plusDays(1);
            dateList.add(begin);
        }
        return dateList;
    }
}
